// Copyright 2017 The Spade Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//use rand::Rng;

use cgmath::{EuclideanSpace, Point2, Point3, Vector3};
use spade::delaunay::{DelaunayTriangulation, DelaunayWalkLocate, FloatDelaunayTriangulation};
use spade::HasPosition;

//use crate::constants::*;
//use noise::{NoiseFn, Seedable};

#[cfg(feature = "interpolate_x")]  //  If interpolating X values...
fn load_data() -> [Point3<f64>; 16] {
    [
        //  Generated by https://docs.google.com/spreadsheets/d/1G9kLS0Es6kwcMA3SC50w5-T-LBYi3NQeY98y7HOAovs/edit#gid=1875321785
        p(1.06194690265487 as f64, 0 as f64, 0 as f64),
        p(44.6017699115044 as f64, 0 as f64, 10.6666666666667 as f64),
        p(84.9557522123894 as f64, 0.888888888888889 as f64, 21.3333333333333 as f64),
        p(120 as f64, 0 as f64, 32 as f64),
        p(1.06194690265487 as f64, 35.5555555555556 as f64, 0 as f64),
        p(42.4778761061947 as f64, 34.6666666666667 as f64, 10.6666666666667 as f64),
        p(82.8318584070797 as f64, 31.1111111111111 as f64, 21.3333333333333 as f64),
        p(118.938053097345 as f64, 25.7777777777778 as f64, 32 as f64),
        p(1.06194690265487 as f64, 70.2222222222222 as f64, 0 as f64),
        p(38.2300884955752 as f64, 67.5555555555556 as f64, 10.6666666666667 as f64),
        p(76.4601769911505 as f64, 67.5555555555556 as f64, 21.3333333333333 as f64),
        p(110.442477876106 as f64, 53.3333333333333 as f64, 32 as f64),
        p(1.06194690265487 as f64, 99.5555555555556 as f64, 0 as f64),
        p(31.858407079646 as f64, 97.7777777777778 as f64, 10.6666666666667 as f64),
        p(64.7787610619469 as f64, 92.4444444444444 as f64, 21.3333333333333 as f64),
        p(98.7610619469027 as f64, 80 as f64, 32 as f64),
    ]
}

#[cfg(feature = "interpolate_y")]  //  If interpolating Y values...
fn load_data() -> [Point3<f64>; 16] {
    [
        //  Generated by https://docs.google.com/spreadsheets/d/1G9kLS0Es6kwcMA3SC50w5-T-LBYi3NQeY98y7HOAovs/edit#gid=1875321785
        p(1.06194690265487 as f64, 0 as f64, 0 as f64),
        p(44.6017699115044 as f64, 0 as f64, 0 as f64),
        p(84.9557522123894 as f64, 0.888888888888889 as f64, 0 as f64),
        p(120 as f64, 0 as f64, 0 as f64),
        p(1.06194690265487 as f64, 35.5555555555556 as f64, 5.33333333333333 as f64),
        p(42.4778761061947 as f64, 34.6666666666667 as f64, 5.33333333333333 as f64),
        p(82.8318584070797 as f64, 31.1111111111111 as f64, 5.33333333333333 as f64),
        p(118.938053097345 as f64, 25.7777777777778 as f64, 5.33333333333333 as f64),
        p(1.06194690265487 as f64, 70.2222222222222 as f64, 10.6666666666667 as f64),
        p(38.2300884955752 as f64, 67.5555555555556 as f64, 10.6666666666667 as f64),
        p(76.4601769911505 as f64, 67.5555555555556 as f64, 10.6666666666667 as f64),
        p(110.442477876106 as f64, 53.3333333333333 as f64, 10.6666666666667 as f64),
        p(1.06194690265487 as f64, 99.5555555555556 as f64, 16 as f64),
        p(31.858407079646 as f64, 97.7777777777778 as f64, 16 as f64),
        p(64.7787610619469 as f64, 92.4444444444444 as f64, 16 as f64),
        p(98.7610619469027 as f64, 80 as f64, 16 as f64),
    ]
}

pub type Delaunay = FloatDelaunayTriangulation<PointWithHeight, DelaunayWalkLocate>;

pub struct PointWithHeight {
    point: Point2<f64>,
    pub height: f64,
    pub gradient: Point2<f64>,
    // We don't need the normal for interpolation purposes. We store it only for
    // visualization.
    pub normal: Vector3<f64>,
}

impl HasPosition for PointWithHeight {
    type Point = Point2<f64>;
    fn position(&self) -> Point2<f64> {
        self.point
    }
}

impl PointWithHeight {
    pub fn position_3d(&self) -> Point3<f64> {
        Point3::new(self.point.x, self.point.y, self.height)
    }

    pub fn new(point: Point2<f64>, height: f64) -> PointWithHeight {
        PointWithHeight {
            point,
            height,
            gradient: Point2::new(0.0, 0.0),
            normal: Vector3::new(0.0, 0.0, 0.0),
        }
    }
}

// Triangulation creation and normal estimation
pub fn generate_random_triangulation() -> Delaunay {
    let mut delaunay = DelaunayTriangulation::with_walk_locate();

    for point in &load_data() {
        delaunay.insert(PointWithHeight::new(Point2::new(point.x, point.y), point.z));
    }
    
    /*
    let mut rng = ::rand::thread_rng();
    let noise = ::noise::OpenSimplex::new().set_seed(rng.gen());
    for _ in 0..NUM_POINTS {
        let x = rng.gen_range(-SAMPLE_REGION, SAMPLE_REGION);
        let y = rng.gen_range(-SAMPLE_REGION, SAMPLE_REGION);
        let height = noise.get([x * FREQUENCY, y * FREQUENCY]) * MAX_HEIGHT;
        // Try out some other height functions, like those:
        // let height = (x * x + y * y) * 0.3;
        // let height = (x * 3.).sin() + (y - 2.).exp();
        delaunay.insert(PointWithHeight::new(Point2::new(x, y), height));
    }
    */

    // Note that, for interpolation, we only need the gradients. For visualization
    // purposes, the normals are also generated and stored within the vertices
    delaunay.estimate_gradients(&(|v| v.height), &(|v, g| v.gradient = g));
    delaunay.estimate_normals(
        &(|v| v.height),
        &(|v: &mut PointWithHeight, n: Point3<_>| v.normal = n.to_vec()),
    );

    delaunay
}

/// Construct a new Point (x,y) with height
fn p(x: f64, y: f64, z: f64) -> Point3<f64> {
    Point3::new(x, y, z)
}